Public Class frmAlgGenetico
    Dim matrizCaminos(,,) As Integer
    Dim resultados As ArrayList
    Dim cantidadCiudades As Integer
    Dim matrizAMostrar(,,) As Integer
    Dim matrizPuntosCiudades(,) As Integer
    Dim ciudadDestino As Integer

    Public minuto As Integer = 0
    Public segundo As Integer = 0
    Public milisegundo As Integer = 0
    

    Public Sub New(ByVal matriz As Array)
        Me.InitializeComponent()
        Me.matrizCaminos = matriz
        Me.cantidadCiudades = Me.matrizCaminos.GetUpperBound(0) + 1
        Me.generarCampos()
    End Sub

    Sub generarCampos()
        'ComboBox Ciudad Destino
        For i As Integer = 1 To Me.cantidadCiudades
            Me.cmbCiudadDestino.Items.Add(i)
        Next
        Me.cmbCiudadDestino.SelectedIndex = 0

        'Tam poblacion automatico
        Me.txbTamPoblacion.Value = Me.CalcularTamanoPoblacion()

        'Cant generaciones
        Me.txbCantGeneraciones.Value = Me.CalcularCantidadGeneraciones()

        'ComboBox Tipos de Seleccion
        Me.cmbTipoSeleccion.Items.Add("Seleccion Por Ranking")
        Me.cmbTipoSeleccion.Items.Add("Control De Copias Esperadas")
        Me.cmbTipoSeleccion.Items.Add("Seleccion por Ruleta")
        Me.cmbTipoSeleccion.Items.Add("Ninguno")
        Me.cmbTipoSeleccion.SelectedIndex = 0

        'ComboBox Tipos de Cruza
        Me.cmbCruza.Items.Add("Combinada")
        Me.cmbCruza.Items.Add("Distribucion Aleatoria")
        Me.cmbCruza.Items.Add("Distribucion SemiAleatoria")
        Me.cmbCruza.Items.Add("Cruza Con Perdidas")
        Me.cmbCruza.SelectedIndex = 0

        'ComboBox Tipos de mutacion
        Me.cmbMutacion.Items.Add("Simple En un Gen")
        Me.cmbMutacion.Items.Add("Gen x Gen")
        Me.cmbMutacion.SelectedIndex = 0
    End Sub

    Sub MostrarResultados()
        'Paso la poblacion al DataGridView
        Dim dtResultado As New DataTable()
        Dim dr As DataRow
        Dim cantResultados = Me.resultados.Count - 1
        Dim individuo As Individuo
        Dim porcentaje As Integer
        Dim generacion As Integer
        Dim poblacion As Integer
        Dim distancia As Integer

        'Le pongo nombre a las columnas
        For i As Integer = 1 To Me.cantidadCiudades
            dtResultado.Columns.Add("P" & i)
        Next
        dtResultado.Columns.Add("Aptitud")
        dtResultado.Columns.Add("Distancia (Km)")
        dtResultado.Columns.Add("Porcentaje")
        dtResultado.Columns.Add("Validez")
        dtResultado.Columns.Add("Generacion")
        dtResultado.Columns.Add("Pob. Final")

        For Each x As ArrayList In Me.resultados
            dr = dtResultado.NewRow()
            individuo = x(0)
            distancia = x(1)
            porcentaje = x(2)
            generacion = x(3)
            poblacion = x(4)


            For i As Integer = 0 To Me.cantidadCiudades - 1
                If i < individuo.Longitud Then
                    dr.Item(i) = individuo.Estructura(i)
                End If
            Next
            dr.Item(Me.cantidadCiudades) = individuo.Aptitud
            dr.Item(Me.cantidadCiudades + 1) = distancia
            dr.Item(Me.cantidadCiudades + 2) = porcentaje

            If individuo.Valido Then
                dr.Item(Me.cantidadCiudades + 3) = "Valido"
            Else
                dr.Item(Me.cantidadCiudades + 3) = "Invalido"
            End If

            dr.Item(Me.cantidadCiudades + 4) = generacion
            dr.Item(Me.cantidadCiudades + 5) = poblacion
            dtResultado.Rows.Add(dr)
        Next
        'Cargo la poblacion al DataGrid
        Me.dgvResultados.DataSource = ""
        Me.dgvResultados.DataSource = dtResultado
    End Sub

    Private Sub radTamAuto_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles radTamAuto.CheckedChanged
        Me.txbTamPoblacion.Enabled = False
        Me.txbTamPoblacion.Value = Me.CalcularTamanoPoblacion()
    End Sub

    Private Sub radTamManual_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles radTamManual.CheckedChanged
        Me.txbTamPoblacion.Enabled = True
    End Sub

    Private Sub radGeneracionesAuto_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles radGeneracionesAuto.CheckedChanged
        Me.txbCantGeneraciones.Enabled = False
        Me.txbCantGeneraciones.Value = Me.CalcularCantidadGeneraciones()
    End Sub

    Private Sub radGeneracionesManual_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles radGeneracionesManual.CheckedChanged
        Me.txbCantGeneraciones.Enabled = True
    End Sub

    Private Sub btgGenerar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btgGenerar.Click
        Dim tamPoblacion As Integer = Me.txbTamPoblacion.Value
        Dim cantGeneraciones As Integer = Me.txbCantGeneraciones.Value
        Dim distanciaMaxima As Integer = 100
        Dim autopista As Integer = 10
        Dim ruta As Integer = 5
        Dim tierra As Integer = 1
        Dim aceptacion As Integer = 80
        Dim pElitista As Integer = Me.txbElitista.Value
        Dim tSeleccion As String = Me.cmbTipoSeleccion.SelectedItem
        Dim tCruza As String = Me.cmbCruza.SelectedItem
        Dim pCruza As Decimal = Me.txbProbCruza.Value
        Dim tMutacion As String = Me.cmbMutacion.SelectedItem
        Dim pMutacion As Decimal = Me.txbProbMutacion.Value

        Me.ciudadDestino = Me.cmbCiudadDestino.SelectedItem

        'Limpiar la tabla de resultados
        Me.dgvResultados.DataSource = ""

        Dim ag As New AlgGenetico(Me.matrizCaminos, tamPoblacion, Me.cantidadCiudades, distanciaMaxima, autopista, ruta, tierra)
        ag.pbGeneracion = Me.pbXOrigen
        ag.pbGeneral = Me.pbGlobal

        Dim count As New System.Diagnostics.Stopwatch()
        count.Start()
        Me.resultados = ag.BuscarOptimos(ciudadDestino, cantGeneraciones, aceptacion, pElitista, tSeleccion, tCruza, pCruza, tMutacion, pMutacion)
        count.Stop()

        'Determino la duracion
        Me.milisegundo = count.ElapsedMilliseconds Mod 1000
        Me.segundo = (count.ElapsedMilliseconds \ 1000) Mod 60
        Me.minuto = (count.ElapsedMilliseconds \ 1000) \ 60

        Me.txbMin.Text = Me.minuto
        Me.txbSeg.Text = Me.segundo
        Me.txbMilSeg.Text = Me.milisegundo
        
        Me.MostrarResultados()
        Me.generarListaCaminos()
    End Sub

    Private Sub btnReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReset.Click
        Me.dgvResultados.DataSource = ""
        Me.cmbCiudadDestino.SelectedIndex = 0
        Me.cmbCruza.SelectedIndex = 0
        Me.cmbMutacion.SelectedIndex = 0
        Me.cmbTipoSeleccion.SelectedIndex = 0

        Me.txbElitista.Value = 10
        Me.txbProbCruza.Value = 0.9
        Me.txbProbMutacion.Value = 0.1

        Me.radGeneracionesAuto.Checked = True
        Me.radTamAuto.Checked = True

        Me.pbGlobal.Value = 0
        Me.pbXOrigen.Value = 0
    End Sub

    Private Function CalcularTamanoPoblacion()
        Return (Me.cantidadCiudades ^ 2) * 5
    End Function

    Private Function CalcularCantidadGeneraciones()
        Return (Me.cantidadCiudades ^ 4)
    End Function

    Private Sub panMapas_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles panMapas.Paint
        Try
            Dim probando As Integer = Me.matrizAMostrar.Length

            Me.matrizPuntosCiudades = Disposicion.CalcularPuntos(Me.panMapas.Width, Me.panMapas.Height)

            PuntosEstaticos.cargarPuntosDinamicos(Me.matrizPuntosCiudades)
            Ciudad.crearCiudades(Me.matrizAMostrar.GetUpperBound(0))
            Camino.crearCaminos(Me.matrizAMostrar)

            Dim i As Integer
            e.Graphics.Clear(Color.White)
            For i = 0 To Camino.caminos.Count - 1
                Camino.caminos(i).dibujar(e.Graphics)
            Next
            For i = 0 To Camino.caminos.Count - 1
                Camino.caminos(i).dibujarDistancias(e.Graphics)
            Next
            For i = 0 To Ciudad.ciudades.Count - 1
                Ciudad.ciudades(i).dibujar(e.Graphics)
            Next
        Catch
        End Try
    End Sub

    Sub generarMatrizAMostrar(ByVal origen As Integer)
        Try
            'Borro la matriz a mostrar
            ReDim Me.matrizAMostrar(Me.cantidadCiudades - 1, Me.cantidadCiudades - 1, 1)
            'Recorro el arreglo de resultados buscando la posicion donde
            'se encuentre el individuo con el origen indicado
            For Each x As ArrayList In Me.resultados
                Dim ind As Individuo = x(0) 'Recupero el individuo
                If ind.Estructura(0) = origen Then
                    Dim anterior As Integer = origen
                    For i As Integer = 1 To ind.Longitud - 1
                        Dim actual As Integer = ind.Estructura(i)
                        'Copio el tramo de la matrizcaminos a la matriz a mostrar
                        Me.matrizAMostrar(anterior - 1, actual - 1, 0) = Me.matrizCaminos(anterior - 1, actual - 1, 0)
                        Me.matrizAMostrar(anterior - 1, actual - 1, 1) = Me.matrizCaminos(anterior - 1, actual - 1, 1)

                        Me.matrizAMostrar(actual - 1, anterior - 1, 0) = Me.matrizCaminos(actual - 1, anterior - 1, 0)
                        Me.matrizAMostrar(actual - 1, anterior - 1, 1) = Me.matrizCaminos(actual - 1, anterior - 1, 1)

                        anterior = actual
                    Next
                    Exit Sub
                End If
            Next
        Catch

        End Try
    End Sub

    Private Sub generarListaCaminos()
        Me.cmbListaOrigenes.Items.Clear()
        Me.cmbListaOrigenes.Items.Add("Todos")
        For Each pos As ArrayList In Me.resultados
            Dim ind As Individuo = pos(0)
            Dim origen As Integer = ind.Estructura(0)
            Me.cmbListaOrigenes.Items.Add(origen)
        Next
        Me.cmbListaOrigenes.SelectedIndex = 0
        Me.lblDestino.Text = "Destino: Ciudad " + Me.ciudadDestino.ToString()
    End Sub

    Private Sub cmbListaOrigenes_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbListaOrigenes.SelectedIndexChanged
        Dim origen As String = Me.cmbListaOrigenes.SelectedItem
        If origen = "Todos" Then
            Me.matrizAMostrar = Me.matrizCaminos.Clone()
            Me.lblDistancia.Text = "Distancia:"
        Else
            Me.generarMatrizAMostrar(origen)
            Dim distancia As Integer = Me.obtenerDistancia(origen)
            Me.lblDistancia.Text = "Distancia: " + distancia.ToString() + " Km"
        End If
        Me.panMapas.Refresh()
    End Sub

    Function obtenerDistancia(ByVal origen As Integer) As Integer
        For Each x As ArrayList In Me.resultados
            Dim ind As Individuo = x(0)
            If ind.Estructura(0) = origen Then
                Return x(1)
            End If
        Next
    End Function
End Class

